#pragma once

#include "zen_vap_helper.h"
#include "zen_vap_node.h"
#include "zen_class.h"
#include <vector>
#include <deque>
#include <functional>

namespace Zen { namespace Vap {

	class Action;

	struct ActionRuntimeStatus
	{
		std::shared_ptr<Action> action;
		std::weak_ptr<Node> node;
		double next_time;
		double time;
		bool valid = true;
	};

	class ActionDispatcher
	{
	public:
		void onUpdate(float interval);

	public: // action.
		void runAction(std::shared_ptr<Action> action, std::shared_ptr<Node> node);
		/**
		 @function stopAction
		 */
		void stopAction(std::shared_ptr<Action> action);

		void removeActionsAttachedOnNode(std::shared_ptr<Node> node);

		void stopAllActions();
	protected:
		void pushInRuntimeQueue_(std::shared_ptr<ActionRuntimeStatus> item);
		std::deque<std::shared_ptr<ActionRuntimeStatus> > m_actions_running;
		std::vector<std::shared_ptr<ActionRuntimeStatus> > m_actions_new;
		double m_running_duration = 0;
		
	};
	/*!
	 @class Action
	 @abstract The base class of Action.
	 */
	class Action : public Object {
	public:
		/*!
		 @function start
		 @abstract This was called (once) when the action started. [to override]
		 @return The delay time before run. <br>
		 if >= 0, delay for run, if the value was less than update interval, it will run in next update. <br>
		 if < 0, action stop.
		 */
		virtual float start() { return m_delay; }

		/*!
		 @function run
		 @abstract This was called in time.
		 @return The interval for next run. see ref function start.
		 */
		virtual float run(float) { return -1; }

		ZEN_MEMBER_COPY_GET_SET(float, m_delay, getDelay, setDelay) = 0.f;
		ZEN_MEMBER_COPY_GET_SET(float, m_interval, getInterval, setInterval) = 0.f;
	};

	/*!
	 @class ActionTimed
	 @abstract The action that has a time life. <br>
	 Basicly, this kind of action has age/lifetime, and it will stop after runout lifetime.
	 */
	class ActionTimed : public Action {
	public:
		virtual float start() override;

		virtual float run(float) override;

		ZEN_MEMBER_COPY_GET_SET(float, m_lifetime, getLifetime, setLifetime) = 0.f;
		ZEN_MEMBER_COPY_GET_ONLY(float, m_age, getAge) = 0.f;
	};

	/*!
	 @class ActionCall
	 @abstract Action to call a function times.
	 */
	class ActionCall : public Action
	{
	public:

		// n = [0, times)
		using Function = std::function<bool(float,int n)>;
	protected:
		ZEN_MEMBER_COPY_GET_SET(Function, m_call, getCallFunc, setCallFunc);
		ZEN_MEMBER_COPY_GET_ONLY(int, m_run_times, getRunTimes) = 0;
		ZEN_MEMBER_COPY_GET_SET(int, m_times, getTargetTimes, setRepeatTimes) = 0;
	public:

		static std::shared_ptr<Action>
		Create(Function call, int times, float delay = 0.f, float interval = 0.f);
	public:

		/*!
		 @function init
		 @abstract set function and repeat times.
		 @param call the Function to call. if the Function returns (<0), the action will stop.
		 @param times if <=0, this will repeat forever; <br> if ==+N repeat N times.
		 */
		void init(Function call, int times);

		virtual float start() override;

		virtual float run(float) override;

	};

	/*!
	 @class ActionRepeat
	 @abstract shell an action, and run that action time and again.
	 */
	class ActionRepeat : public Action
	{
	protected:
		ZEN_MEMBER_COPY_GET_SET(std::shared_ptr<Action>, m_action, getTargetAction, setTargetAction);
		ZEN_MEMBER_COPY_GET_ONLY(int, m_run_times, getRunTimes) = 0;
		ZEN_MEMBER_COPY_GET_SET(int, m_times, getTargetTimes, setRepeatTimes) = 0;
		bool m_is_inner_run = false;
	public:
		static std::shared_ptr<Action>
		Create(std::shared_ptr<Action>, int times, float delay = 0.f, float interval = 0.f);

	public:
		/*!
		 @function init
		 @abstract set action and repeat times.
		 @param action repeated target action.
		 @param times repeat times. <br>
		 if times <= 0 repeat forever.
		 */
		void init(std::shared_ptr<Action> action, int times);

		virtual float run(float) override;

		virtual float start() override;
	};
	/*!
	 @class ActionSequence
	 @abstract Run a sequence of actions.
	 */
	class ActionSequence : public Action
	{
	protected:
		std::vector<std::shared_ptr<Action> > m_actions;
		int m_index = 0;
		bool m_is_inner_run = false;

	public:
		static std::shared_ptr<Action>
		Create(std::vector<std::shared_ptr<Action> >, float delay = 0.f, float interval = 0.f);

	public:
		
		void initActions(std::vector<std::shared_ptr<Action> >);

		void addAction(std::shared_ptr<Action>);

		virtual float run(float) override;

		virtual float start() override;

	};

	/*!
	 @class ActionSequence
	 @abstract This is a partial class for the Actions that bind a view2D node.
	 */
	class PartActionView2D : virtual public Object {
	protected:
		std::weak_ptr<PartOfMatrix2D> m_target;
		void initTarget(std::shared_ptr<PartOfMatrix2D> target);
	public:
		std::shared_ptr<PartOfMatrix2D> getTarget() { return m_target.lock(); }
	};

	/*!
	 @class ActionMove2D
	 @abstract move position. <br>
	 getTarget().pos + (speed * age) + (0.5 * acc * age * age) = to
	 */
	class ActionMove2D : public ActionTimed, public PartActionView2D
	{
	protected:
		ZEN_MEMBER_CONST_GET_ONLY(Vec2, m_speed, getMoveSpeed) = { 0, 0 };
		ZEN_MEMBER_CONST_GET_ONLY(Vec2, m_acc, getMoveAcc) = { 0, 0 };
		Vec2 m_pos_var = { 0, 0 };

	public:
		static std::shared_ptr<Action> Create(std::shared_ptr<PartOfMatrix2D> target, float duration, Vec2 speed, Vec2 acc, float delay = 0.f, float interval = 0.f);

		void initMove(Vec2 speed, Vec2 acc);

		virtual float start() override;

		virtual float run(float interval) override;

	};
	/*!
	 @class ActionScale2D
	 @abstract scale action for view2D node. <br>
	 getTarget().scale + (speed * age) + (0.5 * acc * age * age) = to
	 */
	class ActionScale2D : public ActionTimed, public PartActionView2D
	{
	protected:
		Vec2 m_speed;
		Vec2 m_acc = { 0, 0 };
		Vec2 m_scale_var = { 0, 0 };
		void initScale(Vec2 speed, Vec2 acc);
	public:
		static std::shared_ptr<Action> Create(std::shared_ptr<PartOfMatrix2D> target, float duration, Vec2 speed, Vec2 acc, float delay = 0.f, float interval = 0.f);

	public:

		virtual float run(float interval) override;

		virtual float start() override;
	};

	/*!
	 @class ActionRotate2D
	 @abstract rotation action for view2D node. <br>
	 getTarget().rotate + (angular_speed * age) + (0.5 * acc * age * age) = to
	 */
	class ActionRotate2D : public ActionTimed, public PartActionView2D
	{
	protected:
		float m_angular_speed = 0;
		float m_acc = 0;
		float m_angular_var = 0;
		void initRotate(float velo, float acc);
	public:
		static std::shared_ptr<Action>
		Create(std::shared_ptr<PartOfMatrix2D> target, float duration,float angular_speed, float acc = 0, float delay = 0.f, float interval = 0.f);

	public:

		virtual float run(float interval) override;

		virtual float start() override;
	};

}}
